﻿using System;
using System.Collections.Generic;
using Antlr.Runtime;
using SQLAnalyticLibrary.Expressions;
using SQLAnalyticLibrary.Factory;
using SQLAnalyticLibrary.Expressions.Predicate;

namespace SQLAnalyticLibrary
{
    public class OqlUtil
    {
        private static IDictionary<string, int> _typeMapping = new Dictionary<string, int>();
        static OqlUtil()
        {
            _typeMapping.Add("=", OQLParser.Eq);
            _typeMapping.Add("<>", OQLParser.Neq1);
            _typeMapping.Add("!=", OQLParser.Neq2);
            _typeMapping.Add("<=", OQLParser.Le1);
            _typeMapping.Add("!>", OQLParser.Le2);
            _typeMapping.Add("<", OQLParser.Lt);
            _typeMapping.Add(">=", OQLParser.Ge1);
            _typeMapping.Add("!<", OQLParser.Ge2);
            _typeMapping.Add(">", OQLParser.Gt);
            _typeMapping.Add("LIKE", OQLParser.LIKE);
        }

        private OqlUtil()
        {
        }

        public static QueryStatement CompileQuery(string oql)
        {
            if (string.IsNullOrEmpty(oql)) return null;
            AstParserRuleReturnScope<Expression, IToken> statements = null;

            try
            {
                OQLLexer lex = new OQLLexer(new CaseInsensitiveStringStream(oql));
                CommonTokenStream tokens = new CommonTokenStream(lex);
                OQLParser parser = new OQLParser(tokens);
                parser.TreeAdaptor = new TreeFactory();
                statements = parser.Start();
            }
            catch (Exception e)
            {
                throw e;
            }

            QueryStatement query = null;
            if (statements != null && statements.Tree != null)
            {
                if (!statements.Tree.IsNil) query = statements.Tree as QueryStatement;
                if (query == null && statements.Tree._children != null && statements.Tree._children.Count > 0)
                    query = statements.Tree._children[0] as QueryStatement;
            }
            return query;
        }

        //predicates
        public static Expression CreateIs(string columnName, string predicateText)
        {
            Is exp = new Is(new CommonToken(Is.TreeType));
            Identifier identifier = new Identifier(new CommonToken(Identifier.TreeType, columnName));
            exp.AddChild(identifier);
            Text predicate = new Text(new CommonToken(OQLParser.IS, predicateText));
            exp.AddChild(predicate);
            return exp;
        }
        public static Expression CreateIn(string columnName, IList<string> paramName)
        {
            In exp = new In(new CommonToken(In.TreeType, "IN"));
            Identifier identifier = new Identifier(new CommonToken(Identifier.TreeType, columnName));
            exp.AddChild(identifier);

            if (paramName != null && paramName.Count > 0)
                for (int i = 0; paramName != null && i < paramName.Count; i++)
                {
                    UserVariable variable = new UserVariable(new CommonToken(UserVariable.TreeType, paramName[i]));
                    exp.AddChild(variable);
                }

            return exp;
        }
        public static Expression CreateBetween(string columnName, string lowerParam, string higherParam)
        {
            Between exp = new Between(new CommonToken(Between.TreeType, "BETWEEN"));
            Identifier identifier = new Identifier(new CommonToken(Identifier.TreeType, columnName));
            exp.AddChild(identifier);

            UserVariable variable = new UserVariable(new CommonToken(UserVariable.TreeType, lowerParam));
            exp.AddChild(variable);
            Text text = new Text(new CommonToken(And.TreeType, "AND"));
            exp.AddChild(text);
            variable = new UserVariable(new CommonToken(UserVariable.TreeType, higherParam));
            exp.AddChild(variable);

            return exp;
        }
        public static Expression CreateComparison(string columnName, string paramName, string operatorString)
        {
            Comparison exp = new Comparison(new CommonToken(Comparison.TreeType));
            Identifier identifier = new Identifier(new CommonToken(Identifier.TreeType, columnName));
            exp.AddChild(identifier);

            Text text = new Text(new CommonToken(_typeMapping[operatorString], operatorString));
            exp.AddChild(text);

            UserVariable variable = new UserVariable(new CommonToken(UserVariable.TreeType, paramName));
            exp.AddChild(variable);

            return exp;
        }
        public static Expression CreateLike(string columnName, string paramName)
        {
            return CreateComparison(columnName, paramName, "LIKE");
        }

        //conditions
        public static Expression CreateAnd(Expression left, Expression right)
        {
            if (left == null) return right; //right can also be null.
            if (right == null) return left;
            And exp = new And(new CommonToken(And.TreeType, "AND"));
            exp.AddChild(left);
            exp.AddChild(right);
            return exp;
        }
        public static Expression CreateOr(Expression left, Expression right)
        {
            if (left == null) return right; //right can also be null.
            if (right == null) return left;
            Or exp = new Or(new CommonToken(Or.TreeType, "OR"));
            exp.AddChild(left);
            exp.AddChild(right);
            return exp;
        }
        public static Expression CreateNot(Expression exp)
        {
            if (exp == null) return null;
            Not expNot = new Not(new CommonToken(Not.TreeType, "NOT"));
            expNot.AddChild(exp);
            return expNot;
        }
        public static Expression CreateGroup(Expression exp)
        {
            if (exp == null) return null;
            ConditionGroup expGroup = new ConditionGroup(new CommonToken(ConditionGroup.TreeType));
            expGroup.AddChild(exp);
            return expGroup;
        }

        public static WhereClause CreateWhere()
        {
            IToken whereClauseToken = new CommonToken(WhereClause.TreeType);
            WhereClause clause = new WhereClause(whereClauseToken);
            clause.AddChild(new Text(new CommonToken(OQLParser.WHERE, "WHERE")));
            return clause;
        }
        public static OrderByClause CreateOrderBy()
        {
            return new OrderByClause(new CommonToken(OrderByClause.TreeType), "ORDER BY");
        }
        public static OrderByItem CreateOrderByItem(string columnName, string method)
        {
            if (string.IsNullOrEmpty(columnName)) return null;
            OrderByItem oi = new OrderByItem(new CommonToken(OrderByItem.TreeType));
            Identifier identifier = new Identifier(new CommonToken(Identifier.TreeType, columnName));
            oi.AddChild(identifier);
            if (!string.IsNullOrEmpty(method))
            {
                string m = method.ToLower();
                if (m == "asc" || m == "desc")
                {
                    int type = (m == "asc" ? OQLParser.ASC : OQLParser.DESC);
                    Text text = new Text(new CommonToken(type, method));
                    oi.AddChild(text);
                }
            }
            return oi;
        }
    }
}